using System.Diagnostics;

namespace GeoAzure.Core
{
    internal class IndexEntry<T>
        where T : class
    {
        /// <summary>
        /// Constructs an <see cref="IndexEntry{T}"/> for a leaf node.
        /// </summary>
        /// <param name="element">The element to be indexed.</param>
        /// <param name="bounds"></param>
        public IndexEntry(T element, GeoRectangle bounds)
        {
            Bounds = bounds;
            _element = element;
            _nodeLsn = -1;
        }

        /// <summary>
        /// Constructs an <see cref="IndexEntry{T}"/> for a non-leaf node.
        /// </summary>
        /// <param name="node"></param>
        public IndexEntry(TreeNode<T> node)
        {
            Debug.Assert(node != null);
            Bounds = node.Bounds;
            _element = node;
            _nodeLsn = node.Lsn;
        }

        public GeoRectangle Bounds { get; internal set; }

        private long _nodeLsn;
        public long NodeLsn
        {
            get
            {
                Debug.Assert(_nodeLsn > 0, "Illegal attempt to access the LSN from a leaf index entry.");
                return _nodeLsn;
            }
            private set
            {
                Debug.Assert(_nodeLsn > 0, "Illegal attempt to update an LSN of a leaf entry.");
                Debug.Assert(value >= _nodeLsn, "Updated LSN should be larger than previous LSN.");
                _nodeLsn = value;
            }
        }

        private readonly object _element;
        public TreeNode<T> Node
        {
            get
            {
                Debug.Assert(_nodeLsn > 0, "Illegal attempt to access reference to a child TreeNode from a leaf index entry.");
                return _element as TreeNode<T>;
            }
        }

        public T Element
        {
            get
            {
                Debug.Assert(_nodeLsn == -1, "Illegal attempt to access reference to an indexed element from a non-leaf index entry.");
                return _element as T;
            }
        }

        public bool Update()
        {
            var newBounds = Node.Bounds;
            var previousBounds = Bounds;

            Bounds = newBounds;
            NodeLsn = Node.Lsn;

            return previousBounds != newBounds;
        }
    }
}